﻿using ZyGame.Avatar;
using ZyGame.Execute;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace ZyGame.Drawing
{
    internal class DrawingInstance : Singleton<DrawingInstance>
    {
        private ElementDrawing drawing;
        private void EnsureInitializedGraffiti()
        {
            if (!Client.Avatar.EnsureInitializedAvatarBuilded())
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_INITIALIZED_AVATAR);
                return;
            }
            if (this.drawing != null)
            {
                return;
            }
            Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_INITIALIZED_DRAWING);
        }

        /// <summary>
        /// 部件涂鸦
        /// </summary>
        /// <param name="element"></param>
        public void ElementGraffiti(string data)
        {
            Client.Console.WriteLine(" 进入部件涂鸦步骤：ElementGraffiti");
            if (this.drawing != null && this.drawing.changed != Changed.Saved)
            {
                //todo 先退出涂鸦
                void SaveCompleted(object args)
                {
                    Client.Events.Unregister(EventNames.SAVED_GRAFFITI_DATA_COMPLETED, SaveCompleted);
                    ElementGraffiti(data);
                }
                Client.Events.Register(EventNames.SAVED_GRAFFITI_DATA_COMPLETED, SaveCompleted);
                Client.Events.Notify(EventNames.NOTICE_SAVED_GRAFFITI_DATA);
                return;
            }
            try
            {
                ExitGraffiti();
                ElementData elementData = Newtonsoft.Json.JsonConvert.DeserializeObject<ElementData>(data);
                if (elementData == null)
                {
                    Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_FIND_THE_ELEMENT_DATA + " With Entry Graffitu");
                    return;
                }
                if (elementData.publish_status == (int)PublishState.Drafts)
                {
                    //todo 导入涂鸦数据
                    Client.Web.DownloadData(elementData.texture).Execute(DownloadAssetDataCompleted);
                    void DownloadAssetDataCompleted(Runnable<byte[]> executed)
                    {
                        Client.Events.Register(EventNames.GRAFFITI_INITIALIZED_COMPLETED, Initialized_Completed);
                        Generate(elementData.id, Element.None, executed.result);
                    }
                }
                else
                {
                    Client.Events.Register(EventNames.SET_ELEMENT_DATA_COMPLATED, SetElementCompleted);
                    Client.Avatar.SetElementData(new List<ElementData>() { elementData });

                }
                void SetElementCompleted(object args)
                {
                    Client.Events.Unregister(EventNames.SET_ELEMENT_DATA_COMPLATED, SetElementCompleted);
                    Client.Events.Register(EventNames.GRAFFITI_INITIALIZED_COMPLETED, Initialized_Completed);
                    //todo 初始化涂鸦
                    Generate(string.Empty, elementData.element, null);
                }
                void Initialized_Completed(object args)
                {
                    Client.Events.Unregister(EventNames.GRAFFITI_INITIALIZED_COMPLETED, Initialized_Completed);
                    drawing = (ElementDrawing)args;
                    CameraCtrl.instance.Show(ControllerState.Pen);
                    CameraCtrl.instance.LockControllerType(ControllerState.Pen);
                }
            }
            catch (Exception e)
            {
                Client.Console.WriteError(e);
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, string.Format(ErrorInfo.INITIALIZED_GRAFFITI_FAIL, e.Message));
            }
        }


        /// <summary>
        /// 退出涂鸦
        /// </summary>
        public void ExitGraffiti()
        {
            if (this.drawing == null)
            {
                return;
            }
            this.drawing.Dispose();
            this.drawing = null;
            CameraCtrl.instance.Hide(ControllerState.Pen);
            CameraCtrl.instance.LockControllerType(ControllerState.None);
        }

        /// <summary>
        /// 保存涂鸦数据
        /// </summary>
        /// <param name="name">文件名,如果文件为空则不保存数据</param>
        public void Save(string name)
        {
            this.EnsureInitializedGraffiti();
            Client.Console.WriteLine("保存" + name);
            if (this.drawing == null)
            {
                return;
            }
            this.drawing.Save(name);
            Client.Console.WriteLine("通知保存完成" + name);
        }

        /// <summary>
        /// 发布涂鸦
        /// </summary>
        public void PublishGraffiti(string name)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.Publishing(name);
        }

        /// <summary>
        /// 设置画笔
        /// </summary>
        /// <param name="brush">1:钢笔 2:刷子 3:橡皮檫 4:拖动</param>
        public void SetPaintbrushType(int brush)
        {
            this.EnsureInitializedGraffiti();
            DrawingSetting.instance.SetPaintBrush((PaintBrush)brush);
            CameraCtrl.instance.LockControllerType(ControllerState.Pen);
        }

        /// <summary>
        /// 设置画笔颜色
        /// </summary>
        /// <param name="hexadecimal">颜色值</param>
        public void SetPaintbrushColor(string hexadecimal)
        {
            this.EnsureInitializedGraffiti();
            DrawingSetting.instance.SetPaintbrushColor(hexadecimal.ToColor());
            CameraCtrl.instance.LockControllerType(ControllerState.Pen);
        }

        /// <summary>
        /// 设置画笔大小
        /// </summary>
        /// <param name="width"></param>
        public void SetPaintbrushWidth(float width)
        {
            this.EnsureInitializedGraffiti();
            DrawingSetting.instance.SetBrushWidth(width);
            CameraCtrl.instance.LockControllerType(ControllerState.Pen);
        }

        /// <summary>
        /// 在当前选中的图层中导入涂鸦图片
        /// </summary>
        public void ImportGraffitiTexture()
        {
            this.EnsureInitializedGraffiti();

            void Runnable_OpenFileComplated(object args)
            {
                Client.Events.Unregister(EventNames.OPEN_FILE_COMPLATED, Runnable_OpenFileComplated);
                this.drawing.ImportTexture((byte[])args);
                CameraCtrl.instance.LockControllerType(ControllerState.Pen);
            }

            Client.Events.Register(EventNames.OPEN_FILE_COMPLATED, Runnable_OpenFileComplated);
            Client.File.OpenFileWindow();
        }

        /// <summary>
        /// 新建图层
        /// </summary>
        /// <param name="name">图层名</param>
        public void NewLayer(string name)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.NewLayer(name);
            CameraCtrl.instance.LockControllerType(ControllerState.Pen);
        }

        /// <summary>
        /// 选中图层
        /// </summary>
        /// <param name="name">要选中的图层名</param>
        public void SelectionLayer(string name)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.SelectionLayer(name);
            CameraCtrl.instance.LockControllerType(ControllerState.Pen);
        }

        /// <summary>
        /// 删除选中图层
        /// </summary>
        public void DeleteLayer()
        {
            this.EnsureInitializedGraffiti();
            this.drawing.DeleteLayer();
        }

        /// <summary>
        /// 设置选中图层透明度
        /// </summary>
        /// <param name="alpha">透明度 </param>
        public void SetLayerAlpha(float alpha)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.SetLayerAlpha(alpha);
        }

        /// <summary>
        /// 设置图层缩放大小
        /// </summary>
        /// <param name="size"></param>
        public void SetLayerSize(float size)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.ResizeLayer(size);
        }

        /// <summary>
        /// 撤销
        /// </summary>
        /// <param name="isBackup">0:后退，1:前进</param>
        public void Undo(int isBackup)
        {
            this.EnsureInitializedGraffiti();
            if (isBackup == 0)
            {
                DrawingRecord.Backup();
            }
            else
            {
                DrawingRecord.Forwad();
            }
            drawing.Apply();
        }

        public void SetSort(int index)
        {
            this.EnsureInitializedGraffiti();
            this.drawing.ChangeLayer(index);
        }

        internal static void Generate(string id, Element none, byte[] args)
        {
            if (!Client.Avatar.EnsureInitializedAvatarBuilded())
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_INITIALIZED_AVATAR);
                return;
            }
            CameraCtrl.instance.SetControllrtType(ControllerState.Pen);
            Client.Tools.StartCoroutine(Executed(id, none, args));
        }

        private static IEnumerator Executed(string id, Element element, byte[] bytes)
        {
            ElementDrawing drawing = new()
            {
                element = element,
            };

            if (bytes != null)
            {
                yield return DecompressDrawingData(id, drawing, bytes);
            }
            else
            {
                InitializedDrawing(id, drawing, element);
                if (drawing.layers.Count <= 0)
                {
                    drawing.NewLayer("layer_" + drawing.layers.Count);
                }
                Client.Events.Notify(EventNames.GRAFFITI_INITIALIZED_COMPLETED, drawing);
            }
        }

        private static void InitializedDrawing(string id, ElementDrawing drawing, Element element)
        {
            drawing.component = Client.Avatar.GetElementComponent((int)element);
            if (drawing.component == null)
            {
                Client.Events.Notify(EventNames.ERROR_MESSAGE_NOTICE, ErrorInfo.NOT_FIND_THE_ELEMENT_DATA + " With Initialized");

                return;
            }
            drawing.id = id;
            drawing.original = drawing.component.GetElementTexture();
            drawing.render = new RenderTexture(drawing.original.width, drawing.original.height, 0);
            drawing.drawingRender = new RenderTexture(drawing.original.width, drawing.original.height, 0);
            drawing.render.name = "render";
            drawing.drawingRender.name = "drawing";
            drawing.render.DrawTexture(new Rect(0, 0, drawing.original.width, drawing.original.height), drawing.original, null);
            drawing.component.SetElementTexture(drawing.render);
            Client.Avatar.DisableElement((int)Element.None);
            for (int i = 0; i < AvatarSetting.defaultElements.Length; i++)
            {
                Client.Avatar.EnableElement((int)AvatarSetting.defaultElements[i]);
            }
            Client.Avatar.EnableElement((int)element);
            drawing.gameObject.GenericMeshCollider();
            drawing.gameObject.ToCameraCenter();
            CameraCtrl.instance.SetTarget(drawing.gameObject);
            Client.Tools.AddUpdate(drawing.OnUpdate);

            drawing.Apply();
            return;
        }

        private static IEnumerator DecompressDrawingData(string id, ElementDrawing drawing, byte[] bytes)
        {
            Client.Console.WriteLine("DecompressDrawingData");
            using (BinaryReader reader = new BinaryReader(new MemoryStream(bytes)))
            {
                drawing.element = (Element)reader.ReadByte();
                string data = reader.ReadString();
                ElementData elementData = Newtonsoft.Json.JsonConvert.DeserializeObject<ElementData>(data);

                //if (!Client.Avatar.EnsureAvatarBuildedGroupName(elementData.model_name))
                //{
                //    Client.Console.WriteLine("??");
                //    yield break;
                //}
                Client.Running.TryGetOrCreate(id, out ParallelRunnable executedBuilder);
                Client.Avatar.SetElementData(executedBuilder, elementData);
                yield return executedBuilder.Waitting();
                InitializedDrawing(id, drawing, drawing.element);
                byte layerCount = reader.ReadByte();
                Client.Console.WriteLine(layerCount);
                for (int j = 0; j < layerCount; j++)
                {
                    DrawingData d = DrawingData.GenerateToBinary(reader);
                    drawing.layers.Add(d);
                    Client.Events.Notify(EventNames.CREATE_LAYER_COMPLETED, d.name);
                }
                if (drawing.layers.Count <= 0)
                {
                    drawing.NewLayer("layer_" + drawing.layers.Count);
                }
                drawing.SelectionLayer(drawing.layers.LastOrDefault().name);
                drawing.Apply();
                Client.Events.Notify(EventNames.GRAFFITI_INITIALIZED_COMPLETED, drawing);
                Client.Events.Notify(EventNames.IMPORT_GRAFFITI_DATA_COMPLETED);

            }
        }
    }
}
